const uploadPianke = require("upload-pianke");
const uploadEastday = require("upload-eastday");
const upload7moor = require("upload-7moor");
const del = require("del");
const util = require("util");
const path = require("path");
const fs = require("fs");
const lib = require("./lib");
const PIANKE_SIZE_LIMIT = 900 * 1024 * 1024;
/**
 * 
 * @param {String} full_path 
 * @returns {Promise<{result:Boolean,file_urls:String[],message:String|String[]}>}
 */
function uploadFileByPianke(full_path) {
  return new Promise(async resolve => {
    let exists = await lib.checkExists(full_path);
    if (!exists) {
      return resolve({
        result: false,
        file_urls: [],
        message: `[${full_path}]:this path does not exists`
      })
    }
    let isFile = await lib.checkIsFile(full_path);
    if (!isFile) {
      return resolve({
        result: false,
        file_urls: [],
        message: `[${full_path}]:this path is not a file path`
      })
    }
    let o_checkSize = await lib.checkFileSize(full_path);
    if (!o_checkSize.result) {
      return resolve({
        result: false,
        file_urls: [],
        message: `[${full_path}]:Error when check file size:  ${o_checkSize.message}`
      })
    }
    if (o_checkSize.size <= PIANKE_SIZE_LIMIT) {
      /**@description 说明这是个小文件,注意必须是小于等于号,不能是小于号 */
      let file_urls = [];
      let error_messages = []
      let o_pianke = await uploadPianke.uploadSingle(full_path);
      if (o_pianke.result) {
        file_urls.push(o_pianke.file_url);
      } else {
        error_messages.push(o_pianke.message);
      }
      if (file_urls.length) {
        resolve({
          result: true,
          file_urls: file_urls.map(u => `${u}#filename=${[path.basename(full_path)]}`),
          message: "done"
        });
      } else {
        resolve({
          result: false,
          file_urls: [],
          message: `[${full_path}]:${error_messages}`
        })
      }
    } else {
      let o_split = await lib.splitFileBySize(full_path, PIANKE_SIZE_LIMIT);
      if (!o_split.result) {
        return resolve({
          result: false,
          file_urls: [],
          message: `[${full_path}]:Split failed:${o_split.message}`
        });
      }
      let os_uploaded_after_split = await Promise.all(o_split.splitted_files.map(f => uploadFileByPianke(f)));
      let all_uploaded = !(os_uploaded_after_split.map(o => o.result).includes(false));
      if (!all_uploaded) {
        return resolve({
          result: false,
          file_urls: [],
          message: `[${full_path}]:Not all splitted files were uploaded:${os_uploaded_after_split.filter(o=>!o.result).map(o=>{
            return o.message
          }).join("\n,")}`
        })
      }
      resolve({
        result: true,
        message: "ok",
        file_urls: os_uploaded_after_split.map(o => o.file_urls).reduce((a, b) => a.concat(b)) //simple flatten
      })
    }
  })
}

/**
 * 
 * @param {String} full_path 
 * @param {String} tmp_dir
 * @returns {Promise<{result:Boolean,file_urls:String[],message:String|String[]}>}
 */
function uploadFileByPiankeEastday7moor(full_path, tmp_dir = null) {
  return new Promise(async resolve => {
    let exists = await lib.checkExists(full_path);
    if (!exists) {
      return resolve({
        result: false,
        file_urls: [],
        message: `[${full_path}]:this path does not exists`
      })
    }
    let isFile = await lib.checkIsFile(full_path);
    if (!isFile) {
      return resolve({
        result: false,
        file_urls: [],
        message: `[${full_path}]:this path is not a file path`
      })
    }
    let o_checkSize = await lib.checkFileSize(full_path);
    if (!o_checkSize.result) {
      return resolve({
        result: false,
        file_urls: [],
        message: `[${full_path}]:Error when check file size:  ${o_checkSize.message}`
      })
    }

    if (o_checkSize.size <= PIANKE_SIZE_LIMIT) {
      /**@description 说明这是个小文件,注意必须是小于等于号,不能是小于号 */
      let file_urls = [];
      let error_messages = [];
      let [o_pianke, o_7moor, o_eastday] = await Promise.all([
        uploadPianke.uploadSingle(full_path),
        upload7moor.uploadSingle(full_path),
        uploadEastday.uploadSingle(full_path)
      ]);
      for (let o of [o_pianke, o_7moor, o_eastday]) {
        if (o.result) {
          file_urls.push(o.file_url);
        } else {
          error_messages.push(o.message)
        }
      }
      if (file_urls.length) {
        return resolve({
          result: true,
          file_urls: file_urls.map(u => `${u}#filename=${[path.basename(full_path)]}`),
          message: "done"
        })
      } else {
        return resolve({
          result: false,
          file_urls: [],
          message: `[${full_path}]:${error_messages}`
        })
      }
    } else {
      let o_split = await lib.splitFileBySizeAndMove(full_path, PIANKE_SIZE_LIMIT, tmp_dir);
      if (!o_split.result) {
        return resolve({
          result: false,
          file_urls: [],
          message: `[${full_path}]:Split failed:${o_split.message}`
        });
      }
      let os_uploaded_after_split = await Promise.all(
        o_split.splitted_files.map(
          f => uploadFileByPiankeEastday7moor(f)
        )
      );
      let all_uploaded = !(os_uploaded_after_split.map(o => o.result).includes(false));
      //上传完毕以后,完全可以删除splitted_file
      setTimeout($ => {
        o_split.splitted_files.map(f => del(f)
          .then(ps => console.log(`splitted file ${f}  deleted`))
          .catch(e => console.log(`splitted file ${f}  delete failed : ${e}`))
        )
      }, 10000);
      if (!all_uploaded) {
        return resolve({
          result: false,
          file_urls: [],
          message: `[${full_path}]:Not all splitted files were uploaded:${os_uploaded_after_split.filter(o=>!o.result).map(o=>{
            return o.message
          }).join("\n,")}`
        })
      }

      resolve({
        result: true,
        message: "ok",
        file_urls: os_uploaded_after_split
          .map(o => o.file_urls)
          .reduce((a, b) => a.concat(b))
      })
    }
  })
}



/**
 * @param {String} full_path - the full path of a dir
 * @param {function(file_full_path:String):String} tmp_dir_func 
 * @returns {Promise<{result:Boolean,fail_messages:String[],file_urls:Array<{relative_file_path:String,urls:String[]}>}>}
 */
function uploadDirByPiankeEastay7moor(full_path, tmp_dir_func) {
  return new Promise(async resolve => {
    let o_isDir = await lib.checkIsDir(full_path);
    if (!o_isDir) {
      return resolve({
        result: false,
        fail_messages: [`the gaven path is not a dir (${full_path}) `],
        file_urls: []
      })
    }
    let o_listed = await lib.listDirFiles(full_path, {
      reject: (p) => {
        let b = path.basename(p);
        if ([".git", "node_modules"].includes(b)) {
          return true;
        }
        return false
      }
    });
    if (!o_listed.result) {
      return resolve({
        result: false,
        fail_messages: [`Error when try listing dir(${full_path}) : ${o_listed.message}`],
        file_urls: []
      })
    }
    let failed_messages = [];
    let file_urls = []
    let o_uploaded = await Promise.all(o_listed.listed_files.map(
      /**
       * @param {String} p
       * @returns {Promise<{result:Boolean,fail_message:String,relative_file_path:String,file_urls:String[]}>}
       */
      p => {
        let tmp_dir = null;
        try {
          tmp_dir = tmp_dir_func(p);
        } catch (e) {};
        let relative_path = p.replace(full_path, "");
        return new Promise(async rslv => {
          let o_up = await uploadFileByPiankeEastday7moor(p, tmp_dir);
          if (o_up.result) {
            rslv({
              fail_message: "",
              result: true,
              relative_file_path: relative_path,
              file_urls: o_up.file_urls
            })
          } else {
            rslv({
              fail_message: util.isArray(o_up.message) ? o_up.message.join(",") : o_up.message,
              result: false,
              relative_file_path: relative_path,
              file_urls: []
            })
          }
        })
      }));
    o_uploaded.forEach(o => {
      if (o.result) {
        file_urls.push({
          relative_file_path: o.relative_file_path,
          urls: o.file_urls
        })
      } else {
        failed_messages.push(`Failed to upload ${o.relative_file_path} : ${o.fail_message}`)
      }
    });
    if (o_uploaded.filter(o => !o.result).length == o_uploaded.length) {
      failed_messages.push("All files upload Failed!");
      resolve({
        result: false,
        fail_messages: failed_messages,
        file_urls: file_urls
      })
    } else {
      resolve({
        result: true,
        fail_messages: failed_messages,
        file_urls: file_urls
      })
    }
  })
}

module.exports = {
  uploadFile: uploadPianke,
  uploadFileByPianke: uploadFileByPianke,
  uploadFileByPiankeEastday7moor: uploadFileByPiankeEastday7moor,
  uploadDirByPiankeEastay7moor:uploadDirByPiankeEastay7moor
}